//********************************************************************************************
//
// File : tcp.c implement for Transmission Control Protocol
//
//********************************************************************************************
//
// Copyright (C) 2007
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
// This program is distributed in the hope that it will be useful, but
//
// WITHOUT ANY WARRANTY;
//
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin St, Fifth Floor, Boston, MA 02110, USA
//
// http://www.gnu.de/gpl-ger.html
//
//********************************************************************************************
#include "StrLib.h"
#include "enc28j60.h"
#include "ip.h"
#include "tcp.h"
#include "HostCmd.h"
//********************************************************************************************
//
// +------------+-----------+------------+----------+
// + MAC header + IP header + TCP header + Data ::: +
// +------------+-----------+------------+----------+
//
// TCP Header
//
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +00+01+02+03+04+05+06+07+08+09+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +               Source Port                     +                Destination Port               +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                                        Sequence Number                                        +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                                     Acknowledgment Number                                     +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +Data Offset+reserved+   ECN  +  Control Bits   +                  Window size                  +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                 Checksum                      +                Urgent Pointer                 +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                                   Options and padding :::                                     +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                                             Data :::                                          +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
//
//********************************************************************************************

// global variables ***********************************************************************
U8 cTcp::seqnum=0xa; // my initial tcp sequence number
//static DWORD_BYTES tcp_sequence_number;

//*****************************************************************************************
//
// Function : tcp_get_dlength
// Description : claculate tcp received data length
//
//*****************************************************************************************
U16 cTcp::tcp_get_dlength ( U8 *rxtx_buffer )
{
    int dlength, hlength;

    dlength = ( rxtx_buffer[ cIP::IP_TOTLEN_H_P ] <<8 ) | ( rxtx_buffer[ cIP::IP_TOTLEN_L_P ] );
    dlength -= sizeof(cIP::IP_HEADER);
    hlength = (rxtx_buffer[ TCP_HEADER_LEN_P ]>>4) * 4; // generate len in bytes;
    dlength -= hlength;
    if ( dlength <= 0 )
        dlength=0;

    return ((U16)dlength);
}
//*****************************************************************************************
//
// Function : tcp_get_hlength
// Description : claculate tcp received header length
//
//*****************************************************************************************
U8 cTcp::tcp_get_hlength ( U8 *rxtx_buffer )
{
    return ((rxtx_buffer[ TCP_HEADER_LEN_P ]>>4) * 4); // generate len in bytes;
}
//********************************************************************************************
//
// Function : tcp_puts_data_p
// Description : puts data from program memory to tx buffer
//
//********************************************************************************************
/*
WORD tcp_puts_data_p ( BYTE *rxtx_buffer, PGM_P data, WORD offset )
{
BYTE ch;

while( (ch = pgm_read_byte(data++)) )
{
rxtx_buffer[ TCP_DATA_P + offset ] = ch;
offset++;
}

return offset;
}
*/
//********************************************************************************************
//
// Function : tcp_puts_data
// Description : puts data from RAM to tx buffer
//
//********************************************************************************************
U16 cTcp::tcp_puts_data ( U8 *rxtx_buffer, U8 *data, U16 offset )
{
    while( *data )
    {
        rxtx_buffer[ TCP_DATA_P + offset ] = *data++;
        offset++;
    }

    return offset;
}
//********************************************************************************************
//
// Function : tcp_send_packet
// Description : send tcp packet to network.
//
//********************************************************************************************
void cTcp::tcp_send_packet (
    U8 *rxtx_buffer,
    U16 dest_port,
    U16 src_port,
    U8 flags,
    U8 max_segment_size,
    U8 clear_seqack,
    U16 next_ack_num,
    U16 dlength,
    U8 *dest_mac,
    U8 *dest_ip )
{
    U8 i, tseq;
    U16 ck;

    // generate ethernet header
    cIP::GenerateMacHeader( rxtx_buffer, cIP::MAC_TYPE_IP_VAL, dest_mac );

    // sequence numbers:
    // add the rel ack num to SEQACK
    if ( next_ack_num )
    {
        for( i=4; i>0; i-- )
        {
            next_ack_num = rxtx_buffer [ TCP_SEQ_P + i - 1] + next_ack_num;
            tseq = rxtx_buffer [ TCP_SEQACK_P + i - 1];
            rxtx_buffer [ TCP_SEQACK_P + i - 1] = 0xff & next_ack_num;

            // copy the acknum sent to us into the sequence number
            rxtx_buffer[ TCP_SEQ_P + i - 1 ] = tseq;

            next_ack_num >>= 8;
        }
    }

    // initial tcp sequence number
    // setup maximum segment size
    // require to setup first packet is receive or transmit only
    if ( max_segment_size )
    {
        // initial sequence number
        rxtx_buffer[ TCP_SEQ_P + 0 ] = 0;
        rxtx_buffer[ TCP_SEQ_P + 1 ] = 0;
        rxtx_buffer[ TCP_SEQ_P + 2 ] = seqnum;
        rxtx_buffer[ TCP_SEQ_P + 3 ] = 0;
        seqnum += 2;

        // setup maximum segment size
        rxtx_buffer[ TCP_OPTIONS_P + 0 ] = 2;
        rxtx_buffer[ TCP_OPTIONS_P + 1 ] = 4;
        rxtx_buffer[ TCP_OPTIONS_P + 2 ] = (1408 / 0x100);
        rxtx_buffer[ TCP_OPTIONS_P + 3 ] = (1408 % 0x100);
        // setup tcp header length 24 bytes: 6*32/8 = 24
        rxtx_buffer[ TCP_HEADER_LEN_P ] = 0x60;
        dlength += 4;
    }
    else
    {
        // no options: 20 bytes: 5*32/8 = 20
        rxtx_buffer[ TCP_HEADER_LEN_P ] = 0x50;
    }

    // generate ip header and checksum
    cIP::GenerateIpHeader( rxtx_buffer, ((sizeof(cIP::IP_HEADER) + TCP_HEADER_LEN + dlength)), cIP::IP_PROTO_TCP_V, dest_ip );

    // clear sequence ack number before send tcp SYN packet
    if ( clear_seqack )
    {
        rxtx_buffer[ TCP_SEQACK_P + 0 ] = 's';
        rxtx_buffer[ TCP_SEQACK_P + 1 ] = 'e';
        rxtx_buffer[ TCP_SEQACK_P + 2 ] = 'q';
        rxtx_buffer[ TCP_SEQACK_P + 3 ] = 'u';
    }

    // setup tcp flags
    rxtx_buffer [ TCP_FLAGS_P ] = flags;

    // setup destination port
    rxtx_buffer [ TCP_DST_PORT_H_P ] = cStrLib::GetHigh16(dest_port);
    rxtx_buffer [ TCP_DST_PORT_L_P ] = cStrLib::GetLow16(dest_port);

    // setup source port
    rxtx_buffer [ TCP_SRC_PORT_H_P ] = cStrLib::GetHigh16(src_port);
    rxtx_buffer [ TCP_SRC_PORT_L_P ] = cStrLib::GetLow16(src_port);

    // setup maximum windows size
    rxtx_buffer [ TCP_WINDOWSIZE_H_P ] = ( cStrLib::GetHigh16(cIP::MAX_RXTX_BUFFER - sizeof(cIP::IP_HEADER)- cIP::MAC_HEADER_LEN ) );
    rxtx_buffer [ TCP_WINDOWSIZE_L_P ] = ( cStrLib::GetLow16(cIP::MAX_RXTX_BUFFER - sizeof(cIP::IP_HEADER)- cIP::MAC_HEADER_LEN ) );

    // setup urgend pointer (not used -> 0)
    rxtx_buffer[ TCP_URGENT_PTR_H_P ] = 0;
    rxtx_buffer[ TCP_URGENT_PTR_L_P ] = 0;

    // clear old checksum and calculate new checksum
    rxtx_buffer[ TCP_CHECKSUM_H_P ] = 0;
    rxtx_buffer[ TCP_CHECKSUM_L_P ] = 0;
    // This is computed as the 16-bit one's complement of the one's complement 
    // sum of a pseudo header of information from the 
    // IP header, the TCP header, and the data, padded 
    // as needed with zero bytes at the end to make a multiple of two bytes. 
    // The pseudo header contains the following fields:
    //
    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    // +00+01+02+03+04+05+06+07+08+09+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+
    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    // +                                       Source IP address                                       +
    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    // +                                     Destination IP address                                    +
    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    // +           0           +      IP Protocol      +                    Total length               +
    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    ck = cIP::SoftwareChecksum( &rxtx_buffer[cIP::IP_SRC_IP_P], TCP_HEADER_LEN + dlength + 8, cIP::IP_PROTO_TCP_V + TCP_HEADER_LEN + dlength );
    rxtx_buffer[ TCP_CHECKSUM_H_P ] = cStrLib::GetHigh16(ck);
    rxtx_buffer[ TCP_CHECKSUM_L_P ] = cStrLib::GetLow16(ck);

    // send packet to ethernet media
    cEnc28j60::SendPacket( rxtx_buffer, cIP::MAC_HEADER_LEN + sizeof(cIP::IP_HEADER) + TCP_HEADER_LEN + dlength );
}


//********************************************************************************************
//
// Function : TCP_process
// Description : processing TCP Packages
//
//********************************************************************************************
bool cTcp::TCP_process ( U8 *rxtx_buffer, U8 *dest_mac, U8 *dest_ip )
{
    U16 dlength, rpl_length = 20;
    U16 dest_port, src_port;

    //neu ko phai protocol TCP thi thoat ra.
    if ( rxtx_buffer [ cIP::IP_PROTO_P ] != cIP::IP_PROTO_TCP_V )
        return (false);

    //neu ko dung port TCP thi cung thoat ra nhung return=1 de khoi kiem tra cac protocol khac
    if ( false == cIP::TCPPortCmp(&rxtx_buffer[ TCP_DST_PORT_H_P ]) )
        return (true);

    dest_port = cStrLib::StrToWord(&rxtx_buffer[TCP_SRC_PORT_H_P]);
    src_port = cIP::GetTCPPort();

    // received packet with flags "SYN", let's send "SYNACK"
    if ( (rxtx_buffer[ TCP_FLAGS_P ] & TCP_FLAG_SYN_V) )
    {
        tcp_send_packet (
            rxtx_buffer,
            dest_port,
            src_port,                       // source port
            TCP_FLAG_SYN_V|TCP_FLAG_ACK_V,  // flag
            1,                              // (bool)maximum segment size
            0,                              // (bool)clear sequence ack number
            1,                              // (bool)calculate new seq and seqack number
            0,                              // tcp data length
            dest_mac,                       // server mac address
            dest_ip );                      // server ip address
        return (true);
    }

    // received packet with flags "FIN", let's send "FINACK"
    if ( (rxtx_buffer [ TCP_FLAGS_P ] & TCP_FLAG_ACK_V) )
    {
        // finack, answer with ack
        if ( (rxtx_buffer[TCP_FLAGS_P] & TCP_FLAG_FIN_V) )
        {
            //tcp_send_ack ( rxtx_buffer, dest_mac, dest_ip );
            tcp_send_packet (
                rxtx_buffer,
                dest_port,
                src_port,                           // source port
                TCP_FLAG_ACK_V|TCP_FLAG_FIN_V,      // flag
                0,                                  // (bool)maximum segment size
                0,                                  // (bool)clear sequence ack number
                1,                                  // (bool)calculate new seq and seqack number
                0,                                  // tcp data length
                dest_mac,                           // server mac address
                dest_ip );                          // server ip address

            return (true);
        }


        // PUSH_ACK, answer with PUSH_ACK
        if ( (rxtx_buffer[TCP_FLAGS_P] & TCP_FLAG_PSH_V) )
        {
            // get tcp data length
            dlength = tcp_get_dlength( rxtx_buffer );
            if (dlength==0)
            {
                tcp_send_packet (
                    rxtx_buffer,
                    dest_port,
                    src_port,                   // source port
                    TCP_FLAG_ACK_V,             // flag
                    0,                          // (bool)maximum segment size
                    0,                          // (bool)clear sequence ack number
                    1,                          // (bool)calculate new seq and seqack number
                    0,                          // tcp data length
                    dest_mac,                   // server mac address
                    dest_ip );                  // server ip address

                return (true);
            }

            rpl_length = cHostCmd::CmdProcessor(&rxtx_buffer[TCP_DATA_P], dlength);
            tcp_send_packet (
                rxtx_buffer,
                dest_port,
                src_port,                       // source port
                TCP_FLAG_ACK_V|TCP_FLAG_PSH_V,  // flag
                0,                              // (bool)maximum segment size
                0,                              // (bool)clear sequence ack number
                dlength,                        // (bool)calculate new seq and seqack number
                rpl_length,                     // tcp data length
                dest_mac,                       // server mac address
                dest_ip );                      // server ip address

            return (true);

        }
    }

    // received packet with flags "RST"
    if ( (rxtx_buffer [ TCP_FLAGS_P ] & TCP_FLAG_RST_V) )
    {
        return (true);
    }
    return (true);
}
